package com.demo.pattern.corp.pipeline.route;

import com.demo.pattern.corp.entity.DemoEntity;
import com.demo.pattern.corp.entity.BasePipelineContext;
import com.demo.pattern.corp.pipeline.handle.IContextHandler;
import com.demo.pattern.corp.pipeline.handle.impl.*;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 管道路由的配置
 */
@Configuration
public class PipelineRouteConfig implements ApplicationContextAware {

	/**
	 * 数据类型->管道中处理器类型列表的路由
	 */
	private static final Map<Class<
			? extends BasePipelineContext>,
			List<Class<? extends IContextHandler<? extends BasePipelineContext>>>> PIPELINE_ROUTE_MAP = new HashMap<>(4);

	/*
	 * 在这里配置各种上下文类型对应的处理管道：键为上下文类型，值为处理器类型的列表
	 */
	static {
		PIPELINE_ROUTE_MAP.put(DemoEntity.class,
				Arrays.asList(
						InputDataPreChecker.class,
						ModelInstanceCreator.class,
						BizSideCustomProcessor.class,
						FormInputPreprocessor.class,
						ModelInstanceSaver.class
				));

		// TODO: 2022年4月15日, 015  将来其他 Context 的管道配置
	}

	/**
	 * 在 Spring 启动时，根据路由表生成对应的管道映射关系，
	 * PipelineExecutor 从这里获取处理器列表
	 */
	@Bean("pipelineRouteMap")
	public Map<Class<? extends BasePipelineContext>, List<? extends IContextHandler<? extends BasePipelineContext>>> getHandlerPipelineMap() {
		return PIPELINE_ROUTE_MAP.entrySet()
				.stream()
				.collect(Collectors.toMap(Map.Entry::getKey, this::toPipeline));
	}

	/**
	 * 根据给定的管道中 ContextHandler 的类型的列表，构建管道
	 */
	private List<? extends IContextHandler<? extends BasePipelineContext>> toPipeline(
			Map.Entry<Class<? extends BasePipelineContext>, List<Class<? extends IContextHandler<? extends BasePipelineContext>>>> entry
	) {

		return entry.getValue()
				.stream()
				.map(appContext::getBean)
				.collect(Collectors.toList());
	}

	private ApplicationContext appContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		appContext = applicationContext;
	}
}